<template>
  <div
    ref="pinbox"
    class="vue-pincode-input-wrapper"
  >
    <input
      v-for="(cell, index) in cells"
      :key="cell.key"
      :ref="`${TYPESC.BASE_REF_NAME}${index}`"
      v-model.trim="cell.value"
      v-bind="$attrs"
      class="vue-pincode-input"
      :type="cellsInputTypes[index]"
      @focus="focusedCellIdx = index"
      @keydown.delete="onCellErase(index, $event)"
    >
  </div>
</template>
<script>
// eslint-disable-next-line import/named
import {TYPESCMIX, TYPESC} from "./inputtypes"

export default {
  name: "InputCode",
  mixins: [TYPESCMIX],
  props: {
    value: {
      type: String,
      required: true,
    },
    length: {
      type: Number,
      default: 3,
    },
    autofocus: {
      type: Boolean,
      default: true,
    },
    secure: {
      type: Boolean,
      default: false,
    },
    characterPreview: {
      type: Boolean,
      default: true,
    },
    inputtype: {
      type: String,
      default: TYPESC.DEFAULT_INPUT_TYPE,
      required: false,
    },
    charPreviewDuration: {
      type: Number,
      default: 300,
    }
  },
  data() {
    return {
      focusedCellIdx: 0,
      cells: [],
      watchers: {},
      cellsInputTypes: {},
    }
  },
  computed: {
    pinCodeComputed() {
      return this.cells.reduce((pin, cell) => pin + cell.value, "")
    },
  },
  watch: {
    value(value) {
      if (value) {
        this.onParentValueUpdated()
      } else {
        this.reset()
      }
    },
    length() {
      this.reset()
    },
    pinCodeComputed(val) {
      this.$emit("input", val)
    },
  },
  mounted() {
    this.init()
    this.onParentValueUpdated()
    if (this.autofocus) {
      this.$nextTick(() => {
        this.focusCellByIndex(0)
      })
    }
  },
  methods: {
    /* init stuff */
    init() {
      const inputType = this.getRelevantInputType()
      for (let key = 0; key < this.length; key += 1) {
        this.setCellObject(key)
        this.setCellInputType(key, inputType)
        this.setCellWatcher(key)
      }
    },
    setCellObject(key) {
      this.$set(this.cells, key, {key, value: ""})
    },
    setCellInputType(index, inputType = TYPESC.SECURE_INPUT_TYPE) {
      this.$set(this.cellsInputTypes, index, inputType)
    },
    setCellWatcher(index) {
      const watchingProperty = `cells.${index}.value`
      this.watchers[watchingProperty] = this.$watch(
        watchingProperty,
        (newVal, oldVal) => this.onCellChanged(index, newVal, oldVal),
      )
    },
    /* handlers */
    onParentValueUpdated() {
      if (this.value.length !== this.length) {
        this.$emit("input", this.pinCodeComputed)
        return
      }
      this.value
        .split("")
        .forEach((cell, idx) => {
          this.cells[idx].value = cell || ""
        })
      if (this.value.length === this.length && this.focusedCellIdx === (this.length - 1)) {
        this.onFillingComplete()
      }
    },
    onFillingComplete() {
      let complete_result = this.value
      this.$emit("fillcomplete", complete_result.toUpperCase())
    },
    // eslint-disable-next-line no-unused-vars
    onCellChanged(index, newVal, oldVal) {
      if (!this.isTheCellValid(newVal, false)) {
        this.cells[index].value = ""
        return
      }
      this.focusNextCell()
      /* performing character preview if it's enabled */
      if (this.secure && this.characterPreview) {
        setTimeout(this.setCellInputType, this.charPreviewDuration, index)
      }
    },
    onCellErase(index, e) {
      const isThisCellFilled = this.cells[index].value.length
      if (!isThisCellFilled) {
        this.focusPreviousCell()
        e.preventDefault()
      }
    },
    /* reset stuff */
    reset() {
      this.unwatchCells()
      this.init()
      this.focusCellByIndex()
    },
    unwatchCells() {
      const watchers = Object.keys(this.watchers)
      watchers.forEach(name => this.watchers[name]())
    },
    /* helpers */
    isTheCellValid(cell, allowEmpty) {
      if (!cell) {
        return allowEmpty ? cell === "" : false
      }
      if (this.inputtype === TYPESC.DEFAULT_INPUT_TYPE) {
        return !!cell.match(TYPESC.CELL_DIGIT_REGEX)
      } else if (this.inputtype === TYPESC.INPUT_REFER_TYPE) {
        return !!cell.match(TYPESC.CELL_LETTER_REGEX)
      } else if (this.inputtype === TYPESC.INPUT_HEX_TYPE) {
        return !!cell.match(TYPESC.CELL_HEX_REGEX)
      } else if (this.inputtype === TYPESC.SECURE_INPUT_TYPE) {
        return !!cell.match(TYPESC.CELL_DIGIT_REGEX)
      } else {
        return false
      }
    },
    getRelevantInputType() {
      if (this.secure && !this.characterPreview)
        return TYPESC.SECURE_INPUT_TYPE
      return this.inputtype
    },
    focusPreviousCell() {
      if (!this.focusedCellIdx) return
      this.focusCellByIndex(this.focusedCellIdx - 1)
    },
    focusNextCell() {
      if (this.focusedCellIdx === this.length - 1) return
      this.focusCellByIndex(this.focusedCellIdx + 1)
    },
    focusCellByIndex(index = 0) {
      const ref = `${TYPESC.BASE_REF_NAME}${index}`
      const el = (this.$refs[ref])[0]
      el.focus()
      el.select()
      this.focusedCellIdx = index
    },
    /* set error status */
    setError(bool) {
      const el = this.$refs["pinbox"]
      if (bool) {
        el.classList.add(TYPESC.CLASS_ERROR)
      } else {
        el.classList.remove(TYPESC.CLASS_ERROR)
      }
    }
  },
}
</script>
